# encoding: utf-8
import requests, datetime, pexpect, os, traceback, re

from app.extensions import log, db
from flask_restplus import Resource
from app.extensions.api import Namespace
from app.modules.common.utils import DbCommit, checkToken, recursive_json_loads, runCommand, sendMqMessage
from app.modules.common.schemas import BaseSchema
from . import parameters, schemas, util
from .models import ProjectDeployTime, ProjectInfo, ProjectConfig, ProjectSql, ProjectHandOverRecord, \
    ProjectMergeRecord, ProjectSvn
from config import BaseConfig
from app.modules.common.parameters import BaseParameters
from app.modules.common.beetleHttpUtil import beetlePost
from app.modules.notify.models import NotifyModel
from app.modules.notify.notifier import send_message
from app.modules.operation.resources import addOperationRecord
from app.modules.integration.resources import getIntegrationProjectInfoList
from app.modules.integration.models import IntegrationEditionChengeRecord as changeRecord, IntegrationPlan, \
    IntegrationInfo
from . import TRUNK_LIST, getTrunkList
from .replaceVersion import removeBom, replaceProVersion
from .common import getProByParam
from app.modules.projectPlan.common import getPlanListByParam
from app.modules.zentao.resources import createPlanBuild

api = Namespace('project', description = "工程操作")  # pylint: disable=invalid-name


@api.route('/getProjectDeployTime')
class GetProjectDeployTime(Resource):
    @api.parameters(parameters.ProjectListBaseParameters())
    @api.response(schemas.ProjectDeployTimeListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取各工程在各中心的上线时间列表

        :param args:
        :return:
        """
        log.info("============== start get project deploy time list ==============")
        _integration_id = args['integration_id']
        log.info("============== integration_id is %s ==============" % _integration_id)

        if _integration_id is not None:
            _info = IntegrationInfo.query.filter_by(integration_id = _integration_id).first()
            if _info is None:
                _msg = "集成计划 %s 不存在" % _integration_id
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

            _pro_name_list = []
            for _obj in getIntegrationProjectInfoList(_integration_id, None, True):
                if _obj.trunk_name not in _pro_name_list:
                    _pro_name_list.append(_obj.trunk_name)

            _list = db.session.query(ProjectDeployTime).filter(ProjectDeployTime.trunk_name.in_(_pro_name_list),
                                                               ProjectDeployTime.integration_id == _integration_id).all()
            for _t in _list:
                if len(ProjectConfig.query.filter_by(integration_id = _integration_id,
                                                     trunk_name = _t.trunk_name).all()) > 0:
                    _t.has_config = True
                else:
                    _t.has_config = False
                if len(ProjectSql.query.filter_by(integration_id = _integration_id,
                                                  trunk_name = _t.trunk_name).all()) > 0:
                    _t.has_sql = True
                else:
                    _t.has_sql = False
        else:
            _list = ProjectDeployTime.query.filter_by(integration_id = '1').all()

        return {'success': True, 'msg': '', 'data': _list}


@api.route('/updateProjectDeployUser')
class GetProjectDeployTime(Resource):
    @api.parameters(parameters.UpdateProjectDeployUserParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        修改工程发布负责人
        :param args:
        :return:
        """
        log.info("============== start update project deploy user ==============")
        _trunk_name = args['trunk_name']
        _zone_code = args['zone_code']
        _account = args['account']
        _realname = args['realname']
        _integration_id = args['integration_id']

        _info = ProjectDeployTime.query.filter_by(trunk_name = _trunk_name, zone_code = _zone_code,
                                                  integration_id = _integration_id).first()
        if _info is None:
            _msg = "%s工程 %s 区域 %s 发布记录不存在" % (_integration_id, _trunk_name, _zone_code)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('PROJECT_DEPLOY_USER',
                           '%s|%s|%s|project_deploy_user' % (_integration_id, _trunk_name, _zone_code),
                           _info.deployer_account, _account, 'update project deploy user', **kwargs)

        _info.deployer_account = _account
        _info.deployer_realname = _realname

        return DbCommit()


@api.route('/getProjectEditionList')
class GetProjectEditionList(Resource):
    @api.parameters(parameters.ProjectBaseParameters())
    @api.response(schemas.ProjectEditionListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取各工程版本列表
        :param args:
        :return:
        """
        log.info("============== start get project edition list ==============")
        _project_name = args['project_name']
        log.info("============== project_name is %s ==============" % _project_name)
        # 请求beetle－api获取工程版本列表   区分ad和正常beetle工程

        _info = getProByParam(project_name = _project_name, withCache = True)
        if _info.package_type == 'ad':
            _url = "%s/uCloud/adEditionList?tokenId=%s&projectName=%s" % (
                BaseConfig.BEETLE_API_URL, args['token_id'], BaseConfig.ADFolder[_info.trunk_name][0])
        else:
            _url = "%s/project/editionInfoList?tokenId=%s&projectName=%s" % (
                BaseConfig.BEETLE_API_URL, args['token_id'], _project_name)
        _rtn = recursive_json_loads(requests.post(url = _url).content)
        if _rtn.code != 0 or _rtn.data is None or _rtn.data.list is None:
            _msg = "获取工程 %s 版本列表失败 %s" % (_project_name, _rtn.desc)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        _list = _rtn.data.list
        return {'success': True, 'msg': '', 'data': _list}


@api.route('/updateProjectStatus')
class UpdateProjectStatus(Resource):
    @api.parameters(parameters.UpdateProjectStatusParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        更新工程状态
        :param args:
        :return:
        """
        log.info("============== start update project status ==============")
        _project_name = args['project_name']
        _new_status = args['new_status']
        log.info("============== project_name is %s ==============" % _project_name)
        log.info("============== new_status is %s ==============" % _new_status)
        _info = getProByParam(project_name = _project_name)
        if _info is None:
            _msg = "工程 %s 不存在" % _project_name
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('PROJECT_STATUS', '%s|project_status' % _project_name, _info.project_status, _new_status,
                           'update project status', **kwargs)

        _info.project_status = _new_status
        return DbCommit(cleanPro = True)


@api.route('/updateProjectEdition')
class UpdateProjectEdition(Resource):
    @api.parameters(parameters.UpdateProjectEditionParameters())
    @api.response(schemas.ProjectListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        修改工程各阶段版本
        :param args:
        :return:
        """
        log.info("============== start update project edition status ==============")
        _project_name = args['project_name']
        _plan_id = args['plan_id']
        _edition_num = args['edition_num']
        _type = args['type']
        log.info("============== project_name is %s ==============" % _project_name)
        log.info("============== plan_id is %s ==============" % _plan_id)
        log.info("============== edition_num is %s ==============" % _edition_num)
        log.info("============== type is %s ==============" % _type)
        return updateProjectEdition(_project_name, _plan_id, _edition_num, _type, args['token_id'], **kwargs)


def updateProjectEdition(_project_name, _plan_id, _edition_num, _type, _token_id, **kwargs):
    _info = getProByParam(project_name = _project_name, plan_id = _plan_id)
    if _info is None:
        _msg = "工程 %s 不存在 或 不在项目%s内" % (_project_name, _plan_id)
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}

    _integration = IntegrationPlan.query.filter_by(plan_id = _plan_id).first()
    _integration_id = None
    if _integration is not None:
        _integration_id = _integration.integration_id

    if _type == 'to_test':  # 提测
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程提测版本
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'toTest': 'true',
            }
            _rtn = beetlePost(_token_id, '/uCloud/uCloudToTest', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 提测版本失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}
            _edition_num = _rtn.data.editionNum

        addOperationRecord('TO_TEST', '%s|to_test_edition' % _project_name, _info.to_test_edition,
                           _edition_num, 'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.to_test_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.to_test_edition = _edition_num
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_DEVELOPING'

        # 获取项目信息，禅道id、产品不为空且当天没有提交过，则生成禅道版本
        _today = datetime.date.today()
        _plan_list = getPlanListByParam(plan_id = _plan_id)
        if len(_plan_list) != 0:
            _plan_info = _plan_list[0]
            if _plan_info is not None and _plan_info.zt_id is not None and _plan_info.link_product is not None \
                    and _today != _plan_info.last_build_time and kwargs['login_user_zt_account'] is not None:
                _status = createPlanBuild(_plan_info, _today, **kwargs)
                if _status:
                    _plan_info.last_build_time = _today

        #项目开发测试中
        sendMqMessage('uplan.exchange', {"type": "DEV_PLAN", "map": {"plan_id": _plan_id}})

    elif _type == 'cancel_to_test':  # 取消提测
        if _info.to_test_edition != _edition_num:
            _msg = "工程 %s 版本%s不是已提交测试版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        cancelHandOver(_project_name, _edition_num, 'TEST')

        if _info.isOnBeetle == '0':
            # 请求java后台修改工程提测版本
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'toTest': 'false',
            }
            _rtn = beetlePost(_token_id, '/uCloud/uCloudToTest', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 取消提测失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('CANCEL_TO_TEST', '%s|to_test_edition' % _project_name, _info.to_test_edition, None,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.to_test_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.to_test_edition = None
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_DEVELOPING'
    elif _type == 'test':  # 测试
        # if _info.to_test_edition != _edition_num:
        #     _msg = "工程 %s 版本%s不是已提交测试版本" % (_project_name, _edition_num)
        #     log.error("============== %s ==============" % _msg)
        #     return {'success': False, 'msg': _msg}

        addOperationRecord('TEST', '%s|testing_edition' % _project_name, _info.testing_edition, _edition_num,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.testing_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.testing_edition = _edition_num
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_TESTING'
    elif _type == 'cancel_test':  # 取消测试
        if _info.testing_edition != _edition_num:
            _msg = "工程 %s 版本%s不是测试中版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程版本测试不通过
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'flag': 'false',
            }
            _rtn = beetlePost(_token_id, '/project/updateTestStatus', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 版本测试不通过失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('CANCEL_TEST', '%s|testing_edition' % _project_name, _info.testing_edition, None,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.testing_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.testing_edition = None
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_DEVELOPING'
    elif _type == 'to_integration':  # 转测
        if _edition_num == 'default':
            _msg = "工程 %s 临时版本不能转测" % _project_name
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('TO_INTEGRATION', '%s|to_integration_edition' % _project_name,
                           _info.to_integration_edition, _edition_num, 'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.to_integration_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.to_integration_edition = _edition_num
        if canUpdateProStatus(_info):
            _info.project_status = 'TO_INTEGRATION'
    elif _type == 'cancel_to_integration':  # 取消转测
        if _info.to_integration_edition != _edition_num:
            _msg = "工程 %s 版本%s不是已转测版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        cancelHandOver(_project_name, _edition_num, 'INTEGRATION')
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程版本测试不通过
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'flag': 'false',
            }
            _rtn = beetlePost(_token_id, '/project/updateTestStatus', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 版本测试不通过失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('CANCEL_TO_INTEGRATION', '%s|to_integration_edition' % _project_name,
                           _info.to_integration_edition, None,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.to_integration_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.to_integration_edition = None
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_TESTING'
    elif _type == 'can_integration':  # 可以集成
        # if _info.to_integration_edition != _edition_num:
        #     _msg = "工程 %s 版本%s不是已转测版本" % (_project_name, _edition_num)
        #     log.error("============== %s ==============" % _msg)
        #     return {'success': False, 'msg': _msg}

        addOperationRecord('CAN_INTEGRATION', '%s|integration_edition' % _project_name, _info.integration_edition,
                           _edition_num,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.integration_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.integration_edition = _edition_num
        if canUpdateProStatus(_info):
            _info.project_status = 'TO_INTEGRATION'
    elif _type == 'can_not_integration':  # 不可以集成
        if _info.to_integration_edition != _edition_num:
            _msg = "工程 %s 版本%s不是已转测版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('CAN_NOT_INTEGRATION', '%s|to_integration_edition' % _project_name,
                           _info.to_integration_edition, None, 'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.to_integration_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.to_integration_edition = None
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_TESTING'
    elif _type == 'integration':  # 集成测试
        # if _info.integration_edition != _edition_num:
        #     _msg = "工程 %s 版本%s不是可以集成版本" % (_project_name, _edition_num)
        #     log.error("============== %s ==============" % _msg)
        #     return {'success': False, 'msg': _msg}

        addOperationRecord('INTEGRATIONING', '%s|integrationing_edition' % _project_name,
                           _info.integrationing_edition,
                           _edition_num, 'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.integrationing_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.integrationing_edition = _edition_num
        if canUpdateProStatus(_info):
            _info.project_status = 'INTEGRATIONING'
    elif _type == 'cancel_integration':  # 取消集成测试
        if _info.integrationing_edition != _edition_num:
            _msg = "工程 %s 版本%s不是集成测试中版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程版本测试不通过
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'flag': 'false',
            }
            _rtn = beetlePost(_token_id, '/project/updateTestStatus', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 版本测试不通过失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('CANCEL_INTEGRATION', '%s|integrationing_edition' % _project_name,
                           _info.integrationing_edition, None, 'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.integrationing_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.integrationing_edition = None
        if canUpdateProStatus(_info):
            _info.project_status = 'PRO_TESTING'
    elif _type == 'hand_over':  # 交付运维
        # if _info.integrationing_edition != _edition_num:
        #     _msg = "工程 %s 版本%s不是集成测试中版本" % (_project_name, _edition_num)
        #     log.error("============== %s ==============" % _msg)
        #     return {'success': False, 'msg': _msg}
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程测试通过版本
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'flag': 'true',
            }
            _rtn = beetlePost(_token_id, '/project/updateTestStatus', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 测试通过版本失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('HAND_OVER', '%s|hand_over_edition' % _project_name, _info.hand_over_edition,
                           _edition_num,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.hand_over_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.hand_over_edition = _edition_num
        _info.project_status = 'HAND_OVER'
    elif _type == 'cancel_hand_over':  # 取消交付
        if _info.hand_over_edition != _edition_num:
            _msg = "工程 %s 版本%s不是已交付版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        if _info.isOnBeetle == '0':
            # 请求java后台修改工程版本测试不通过
            _param = {
                'projectName': _project_name,
                'editionNum': _edition_num,
                'flag': 'false',
            }
            _rtn = beetlePost(_token_id, '/project/updateTestStatus', _param)
            if _rtn.code != 0:
                _msg = "调用beetle修改工程 %s 版本测试不通过失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

        addOperationRecord('CANCEL_HAND_OVER', '%s|hand_over_edition' % _project_name, _info.hand_over_edition,
                           None,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.hand_over_edition,
                        _info.project_status, _project_name, None, _info.project_status,
                        _type, **kwargs)

        _info.hand_over_edition = None
        _info.project_status = 'INTEGRATIONING'
    elif _type == 'online':  # 上线
        if _info.hand_over_edition != _edition_num:
            _msg = "工程 %s 版本%s不是已交付版本" % (_project_name, _edition_num)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('ONLINE', '%s|online_edition' % _project_name, _info.online_edition, _edition_num,
                           'update project edition status', **kwargs)
        addChangeRecord(_integration_id, _plan_id, _info.trunk_name, _project_name, _info.online_edition,
                        _info.project_status, _project_name, _edition_num, _info.project_status,
                        _type, **kwargs)

        _info.online_edition = _edition_num
        _info.project_status = 'ONLINE'
    else:
        _msg = "参数type %s 错误" % _type
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}

    _result = DbCommit(cleanPro = True)
    if not _result['success']:
        return _result

    # 发送ws消息
    _notify = NotifyModel('plan_%s_project' % _info.plan_id, _info.to_dict())
    send_message(_notify)
    # 如果项目已经集成，同时发送集成消息
    if _integration_id is not None:
        _intNotify = NotifyModel('plan_%s_project' % _integration_id, _info.to_dict())
        send_message(_intNotify)

    _list = [_info]

    return {'success': True, 'msg': "", 'data': _list}


def canUpdateProStatus(info):
    ST = ['PRO_MERGED', 'INTEGRATIONING', 'BE_CANCEL', 'HAND_OVER', 'ONLINE']
    status = info.project_status
    if status in ST:
        return False
    else:
        return True


@api.route('/getTrunkProjectList')
class GetTrunkProjectList(Resource):
    @api.parameters(BaseParameters())
    @api.response(schemas.ProjectWithJarListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取主干工程列表
        :param args:
        :return:
        """
        log.info("============== start query trunk project list ==============")

        _trunkProject = []
        _jarProject = []

        _trunkNameList = []
        _jarNameList = []

        for _key in TRUNK_LIST:
            if TRUNK_LIST[_key] == 'maven_jar':
                _jarProject.append(ProjectInfo(project_name = _key, package_type = TRUNK_LIST[_key]))
                _jarNameList.append(_key)
            else:
                _trunkProject.append(ProjectInfo(project_name = _key, package_type = TRUNK_LIST[_key]))
                _trunkNameList.append(_key)

        return {'success': True, 'msg': '', 'data': {'trunkProject': _trunkProject, 'jarProject': _jarProject}}


@api.route('/getProjectOnlineVersion')
class GetProjectOnlineVersion(Resource):
    @api.parameters(parameters.ProjectOnlineVersionParameters())
    @api.response(schemas.ProjectOnlineVersionListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取工程线上版本
        :param args:
        :return:
        """
        log.info("============== start query project online version ==============")
        _pros = args['project_list']
        log.info("============== project_list is %s ==============" % _pros)
        if _pros is None:
            _msg = "工程名称列表不能为空"
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        _project_list = eval(args['project_list'])

        if type(_project_list) is not list:
            _msg = "this project_list %s is not list" % _project_list
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        _param = {
            'apps': _pros,
            'type': 'product'
        }
        _rtn = beetlePost(args['token_id'], '/uCloud/getProjectReleaseVersions', _param)
        if _rtn.code != 0:
            _msg = "获取工程 %s 线上版本失败,error:%s" % (_pros, _rtn.desc)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        return {'success': True, 'msg': '', 'data': _rtn.data}


@api.route('/refreshTrunkList')
class RefreshTrunkList(Resource):
    @api.parameters(BaseParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        刷新主干工程列表缓存
        :param args:
        :return:
        """
        log.info("============== start refresh trunk project list ==============")
        getTrunkList()
        return {'success': True, 'msg': ''}


def addChangeRecord(_integration_id, _plan_id, _trunk_name, _old_project, _old_edition, _old_status, _new_project,
                    _new_edition, _new_status, _key, **kwargs):
    if _integration_id is None:
        return

    _integrationinfo = IntegrationInfo.query.filter_by(integration_id = _integration_id).first()
    # if _integrationinfo.integration_status != 'HAND_OVER' and _integrationinfo.integration_status != 'ONLINE':
    #     return
    is_after_hand = "1"
    if _integrationinfo.integration_status == 'HAND_OVER' or _integrationinfo.integration_status == 'ONLINE':
        is_after_hand = "0"

    _info = changeRecord(integration_id = _integration_id, plan_id = _plan_id, user_role = kwargs['login_user_role'],
                         user_id = kwargs['login_user_id'], user_realname = kwargs['login_user_name'],
                         trunk_name = _trunk_name, old_project = _old_project, old_edition = _old_edition,
                         old_status = _old_status, new_project = _new_project, new_edition = _new_edition,
                         new_status = _new_status, is_after_hand = is_after_hand, key = _key,
                         integration_status = _integrationinfo.integration_status)
    db.session.add(_info)
    DbCommit()


@api.route('/getProjectInfo')
class GetProjectInfo(Resource):
    @api.parameters(parameters.ProjectBaseParameters())
    @api.response(schemas.ProjectSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取工程信息
        :param args:
        :return:
        """
        log.info("============== start get project info ==============")
        _project_name = args['project_name']

        _info = getProByParam(project_name = _project_name, withCache = True)
        if _info is None:
            _msg = '工程%s不存在' % _project_name
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
        return {'success': True, 'msg': '', 'data': _info}


@api.route('/getPHORecordList')
class GetPHORecordList(Resource):
    @api.parameters(parameters.GetPHORecordListParameters())
    @api.response(schemas.ProjectHandOverRecordListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取工程交付记录
        :param args:
        :return:
        """
        _project_name = args['project_name']
        _type = args['type']
        _edition_num = args['edition_num']
        _start_date = args['start_date']
        _end_date = args['end_date']

        _query = ProjectHandOverRecord.query.filter_by(project_name = _project_name, type = _type)
        if _edition_num is not None:
            _query = _query.filter_by(edition_num = _edition_num)
        if _start_date is not None:
            _query = _query.filter(ProjectHandOverRecord.create_date >= _start_date)
        if _end_date is not None:
            _query = _query.filter(ProjectHandOverRecord.create_date <= _end_date)

        _list = _query.order_by(ProjectHandOverRecord.create_date.desc()).all()
        return {'success': True, 'msg': '', 'data': _list}


@api.route('/savePHORecord')
class SavePHORecord(Resource):
    @api.parameters(parameters.SavePHORecordParameters())
    @api.response(schemas.ProjectListSchema(many = False))
    @checkToken
    def post(self, args, **kwargs):
        """
        保存工程交付记录
        :param args:
        :return:
        """
        _project_name = args['project_name']
        _edition_num = args['edition_num']
        _type = args['type']
        _config_desc = args['config_desc']
        _sql_desc = args['sql_desc']
        _other_desc = args['other_desc']
        _is_handle_bug = args['is_handle_bug']

        _pro = getProByParam(project_name = _project_name, withCache = True)
        if _pro is None:
            _msg = '工程%s不存在' % _project_name
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        _info = ProjectHandOverRecord(project_name = _project_name, hand_id = kwargs['login_user_id'],
                                      hand_realname = kwargs['login_user_name'], config_desc = _config_desc,
                                      sql_desc = _sql_desc, other_desc = _other_desc, type = _type,
                                      edition_num = _edition_num, is_handle_bug = _is_handle_bug)
        db.session.add(_info)

        _t = None
        if _type == 'TEST':
            _t = 'to_test'
        elif _type == 'INTEGRATION':
            _t = 'to_integration'

        DbCommit()
        return updateProjectEdition(_pro.project_name, _pro.plan_id, _edition_num, _t, args['token_id'],
                                    **kwargs)


@api.route('/updatePHORecordStatus')
class UpdatePHORecordStatus(Resource):
    @api.parameters(parameters.UpdatePHORecordStatusParameters())
    @api.response(schemas.ProjectListSchema(many = False))
    @checkToken
    def post(self, args, **kwargs):
        """
        修改工程交付记录状态  工程测试 进集成 集成测试
        :param args:
        :return:
        """
        _id = args['id']
        _is_can_integration = args['is_can_integration']
        _is_integration_test = args['is_integration_test']
        _is_function_test = args['is_function_test']

        _info = ProjectHandOverRecord.query.filter_by(id = _id).first()
        if _info is None:
            _msg = '记录%s不存在' % _id
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        _pro = getProByParam(project_name = _info.project_name, withCache = True)
        if _pro is None:
            _msg = '工程%s不存在' % _pro.project_name
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}

        _type = None
        if _is_can_integration is not None:
            _info.is_can_integration = _is_can_integration
            if _is_can_integration == '0':  # 可集成
                _type = 'can_integration'
            elif _is_can_integration == '1':  # 不可集成
                _type = 'can_not_integration'
        elif _is_integration_test is not None:
            _info.is_integration_test = _is_integration_test
            _type = 'integration'
        elif _is_function_test is not None:
            _info.is_function_test = _is_function_test
            _type = 'test'

        DbCommit()

        return updateProjectEdition(_pro.project_name, _pro.plan_id, _info.edition_num, _type, args['token_id'],
                                    **kwargs)


def cancelHandOver(project_name, edition_num, type):
    _list = ProjectHandOverRecord.query.filter_by(project_name = project_name, edition_num = edition_num,
                                                  type = type).all()
    if len(_list) > 0:
        for _record in _list:
            _record.is_cancel = '0'
        _rtn = DbCommit()
        log.info('app.modules.project.resources.cancelHandOver.project_name:%s,edition_num:%s,type:%s, 返回: %s' % (
            project_name, edition_num, type, _rtn))


@api.route('/mergeProToTrunk')
class MergeProToTrunk(Resource):
    @api.parameters(parameters.MergeProToTrunkParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def post(self, args, **kwargs):
        """
        合并分支代码到主干
        :param args:
        :return:
        """
        _integration_id = args['integration_id']
        _project_name = args['project_name']
        _type = args['type']

        try:
            if _type != "svn" and _type != "local":
                _msg = "参数type错误"
                log.error("============== %s ==============" % _msg)
                updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                return {'success': False, 'msg': _msg}

            # 集成计划是否存在
            _info = IntegrationInfo.query.filter_by(integration_id = _integration_id).first()
            if _info is None:
                _msg = '集成计划%s不存在' % _integration_id
                log.error("============== %s ==============" % _msg)
                updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                return {'success': False, 'msg': _msg}
            # 工程是否存在
            _pro = getProByParam(project_name = _project_name, withCache = True)
            if _pro is None:
                _msg = '工程%s不存在' % _project_name
                log.error("============== %s ==============" % _msg)
                updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                return {'success': False, 'msg': _msg}

            if _pro.isOnBeetle == '1':
                _msg = '工程%s没有用beetle管理,需要手动合并主干.' % _project_name
                log.error("============== %s ==============" % _msg)
                updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                return {'success': False, 'msg': _msg}

            _param = {
                'projectName': _project_name
            }
            _rtn = beetlePost(args['token_id'], '/project/getProject', _param)
            if _rtn.code != 0:
                _msg = "调用beetle获取工程 %s 信息失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                return {'success': False, 'msg': _msg}
            _edition = None
            # 版本是否存在 区分jar工程  jar工程没有测试通过版本
            if TRUNK_LIST[_pro.trunk_name] == 'maven_jar':
                _edition = "default"
            else:
                if _pro.hand_over_edition is None or _pro.hand_over_edition == '':
                    _msg = '工程%s交付版本为空' % _project_name
                    log.error("============== %s ==============" % _msg)
                    updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                    return {'success': False, 'msg': _msg}
                _param = {
                    'trunkName': _pro.trunk_name
                }
                _rtn = beetlePost(args['token_id'], '/projectREST/getProTestPassVersion', _param)
                if _rtn.code != 0:
                    _msg = "调用beetle获取工程 %s 测试通过版本失败,error:%s" % (_pro.trunk_name, _rtn.desc)
                    log.error("============== %s ==============" % _msg)
                    updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                    return {'success': False, 'msg': _msg}

                if _rtn.data.projectName != _project_name:
                    _msg = "工程%s的测试通过分支不是%s,测试通过分支为%s" % (_pro.trunk_name, _project_name, _rtn.data.projectName)
                    log.error("============== %s ==============" % _msg)
                    updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                    return {'success': False, 'msg': _msg}

                if _rtn.data.editionNum != _pro.hand_over_edition:
                    _msg = "工程%s的测试通过版本不是%s,测试通过版本为%s" % (_pro.trunk_name, _pro.hand_over_edition, _rtn.data.editionNum)
                    log.error("============== %s ==============" % _msg)
                    updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                    return {'success': False, 'msg': _msg}

                if not _rtn.data.isEditionExist:
                    _msg = "工程%s的测试通过版本%s已不存在" % (_pro.trunk_name, _pro.hand_over_edition)
                    log.error("============== %s ==============" % _msg)
                    updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
                    return {'success': False, 'msg': _msg}
                _edition = _pro.hand_over_edition
            # 工程主干目录存在时 打tag
            _trunk_svn = util.getProjectSvnUrl(_pro.trunk_name, withTrunk = True)
            _sh = '/bin/bash -c "svn info %s"' % _trunk_svn
            output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 600)
            if status == 0:
                _rtn = tagTrunk(_pro.trunk_name, _info.integration_name, _trunk_svn)
                if not _rtn['success']:
                    updatePorMergeRecord(_project_name, _integration_id, _rtn)
                    return _rtn
            else:
                _msg = "工程%s主干目录%s不存在" % (_project_name, _trunk_svn)
                log.error("============== %s ==============" % _msg)
                return {'success': False, 'msg': _msg}

            if TRUNK_LIST[_pro.trunk_name] == 'ant_android' or _type == "svn":
                _rtn = submitToTrunkByCopy(_pro.trunk_name, _project_name, _edition, _info.integration_name,
                                           _pro.vcs_path, _trunk_svn, _pro.plan_name, kwargs['login_user_name'])
            else:
                _rtn = submitToTrunkByUpload(_project_name, _edition, args['token_id'], _trunk_svn,
                                             getCommitDesc(integration_name = _info.integration_name,
                                                           plan_name = _pro.plan_name,
                                                           user_name = kwargs['login_user_name'],
                                                           svn_from = _pro.vcs_path))
                if not _rtn['success']:
                    updatePorMergeRecord(_project_name, _integration_id, _rtn)
                    return _rtn
                _rtn = changePomVersionAndCommit(_pro.trunk_name, _project_name, _edition, _trunk_svn,
                                                 getCommitDesc(integration_name = _info.integration_name,
                                                               plan_name = _pro.plan_name,
                                                               user_name = kwargs['login_user_name'],
                                                               svn_from = _pro.vcs_path))

            updatePorMergeRecord(_project_name, _integration_id, _rtn)
            return _rtn
        except BaseException:
            _msg = "工程%s合并主干异常.e:%s" % (_project_name, traceback.format_exc())
            log.error("============== %s ==============" % _msg)
            updatePorMergeRecord(_project_name, _integration_id, {'success': False, 'msg': _msg})
            return {'success': False, 'msg': _msg}


# 将当前trunk代码打tag
def tagTrunk(_trunk_name, _integration_name, _trunk_svn):
    _tag_svn = "%s/tags/%s-%s-%s" % (util.getProjectSvnUrl(_trunk_name), _trunk_name, _integration_name,
                                     datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
    # 判断tag是否已存在
    _sh = '/bin/bash -c "svn list %s/tags | grep %s"' % (util.getProjectSvnUrl(_trunk_name), _integration_name)
    log.info('============== 检查是否工程%s已经存在tag.%s ==============' % (_trunk_name, _sh))
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        _list = str(output).split('\r\n')
        if len(_list) != 0:
            _msg = '工程%s存在tag%s' % (_trunk_name, _integration_name)
            log.info('============== %s ==============' % _msg)
            return {'success': True, 'msg': ''}
    else:
        _msg = '检查是否工程%s已经存在tag%s异常, cause: %s.' % (_trunk_name, _integration_name, output)
        log.info('============== %s ==============' % _msg)

    _sh = '/bin/bash -c "svn copy %s %s -m \'集成计划%s合并主干打tag\' "' % (_trunk_svn, _tag_svn, _integration_name)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 600)
    if status == 0:
        _msg = "为集成计划%s工程%s主干打tag成功." % (_integration_name, _trunk_name)
        log.info("============== %s ==============" % _msg)
        return {'success': True, 'msg': ''}
    else:
        _msg = "为集成计划%s工程%s主干打tag失败. %s" % (_integration_name, _trunk_name, output)
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}


# 将分支拷贝到trunk 将分支代码下载到本地 然后上传到trunk
def submitToTrunkByCopy(_trunk_name, _project_name, _edition, _integration_name, _svn_branch, _trunk_svn, _plan_name,
                        _user_name):
    _path = '%s/projects/%s/%s/%s/temp' % (BaseConfig.BEETLE_BASE_PATH, _trunk_name, _project_name, _edition)
    _sh = '/bin/bash -c "mkdir -p %s && cd %s && rm -rf trunk branch && svn co %s trunk > /dev/null && svn export %s branch > /dev/null && cd trunk ' \
          '&& ls | xargs svn delete > /dev/null && cd ../ && cp -R branch/* trunk/ "' % (
              _path, _path, _trunk_svn, _svn_branch)

    _rtn = runCommand(_sh)
    if not _rtn['success']:
        _msg = "为集成计划%s工程%s拷贝分支%s至主干失败. %s" % (_integration_name, _trunk_name, _svn_branch, _rtn['msg'])
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}

    changePomVersion(_trunk_name, _project_name, _edition)
    _sh = '/bin/bash -c "cd %s/trunk && svn add * > /dev/null && svn ci -m \'%s\' > /dev/null && cd ../ && rm -rf trunk branch "' \
          % (_path, getCommitDesc(integration_name = _integration_name, plan_name = _plan_name, user_name = _user_name,
                                  svn_from = _svn_branch))
    _rtn = runCommand(_sh)
    if not _rtn['success']:
        _msg = "为集成计划%s工程%s拷贝分支%s至主干失败. %s" % (_integration_name, _trunk_name, _svn_branch, _rtn['msg'])
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}
    return {'success': True, 'msg': ''}


# 将备份代码上传到trunk
def submitToTrunkByUpload(_project_name, _edition, _token_id, _trunk_svn, _desc):
    _param = {
        'projectName': _project_name,
        'editionNum': _edition,
        'trunkUrl': _trunk_svn,
        'desc': _desc
    }
    _rtn = beetlePost(_token_id, '/projectREST/cpCodeToTrunk', _param)
    if _rtn.code != 0:
        _msg = "调用beetle将工程%s版本%s的代码上传至%s失败,error:%s" % (_project_name, _edition, _trunk_svn, _rtn.desc)
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}
    return {'success': True, 'msg': ''}


def getCommitDesc(integration_name = None, plan_name = None, user_name = None, svn_from = None):
    _desc = '[集成计划]:%s\n[项目名称]:%s\n[修改人]:%s\n[来源]:%s' % (integration_name, plan_name, user_name, svn_from)
    return _desc


# 修改maven工程pom文件version为release
def changePomVersion(_trunk_name, _project_name, _edition):
    if TRUNK_LIST[_trunk_name] != 'maven_jar' and TRUNK_LIST[_trunk_name] != 'maven_war' and TRUNK_LIST[
        _trunk_name] != 'spring_boot_jar':
        return
    _path = '%s/projects/%s/%s/%s/temp' % (BaseConfig.BEETLE_BASE_PATH, _trunk_name, _project_name, _edition)
    _pomfile = '%s/trunk/pom.xml' % _path

    if not os.path.exists(_pomfile):
        _msg = "工程%分支%s没有pom文件,不修改版本." % (_trunk_name, _project_name)
        log.error("============== %s ==============" % _msg)
        return

    removeBom(_pomfile)
    replaceProVersion(_pomfile, None, isAddProperties = False, isUpdate = True)
    return


# 修改maven工程pom文件version为release，并提交svn
def changePomVersionAndCommit(_trunk_name, _project_name, _edition, _trunk_svn, _desc):
    if TRUNK_LIST[_trunk_name] != 'maven_jar' and TRUNK_LIST[_trunk_name] != 'maven_war' and TRUNK_LIST[
        _trunk_name] != 'spring_boot_jar':
        return {'success': True, 'msg': ''}
    _path = '%s/projects/%s/%s/%s/temp' % (BaseConfig.BEETLE_BASE_PATH, _trunk_name, _project_name, _edition)
    _pomfile = '%s/trunk/pom.xml' % _path
    _sh = '/bin/bash -c "mkdir -p %s && cd %s && svn co --depth=empty %s trunk && cd trunk && svn up pom.xml " ' % (
        _path, _path, _trunk_svn)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 600)
    if status == 0:
        if not os.path.exists(_pomfile):
            _msg = "工程%分支%s没有pom文件,不修改版本." % (_trunk_name, _project_name)
            log.error("============== %s ==============" % _msg)
            return {'success': True, 'msg': _msg}

        removeBom(_pomfile)
        replaceProVersion(_pomfile, None, isAddProperties = False, isUpdate = True)
        _sh = '/bin/bash -c "cd %s/trunk && svn commit -m \'%s\' && rm -rf %s/trunk " ' % (_path, _desc, _path)
        output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
        if status == 0:
            log.info("============== 合并工程%s至主干,修改pom文件,提交svn完成 ==============" % _project_name)
            return {'success': True, 'msg': ''}
        else:
            _msg = "合并工程%s至主干,修改pom文件,提交svn异常. %s" % (_project_name, output)
            log.error("============== %s ==============" % _msg)
            return {'success': False, 'msg': _msg}
    else:
        _msg = "检出工程%s主干pom文件失败. %s" % (_trunk_name, output)
        log.error("============== %s ==============" % _msg)
        return {'success': False, 'msg': _msg}


def updatePorMergeRecord(_project_name, _integration_id, _rtn):
    status = "1"
    if _rtn['success']:
        status = "0"
    _info = ProjectMergeRecord.query.filter_by(project_name = _project_name).first()
    if _info is None:
        db.session.add(
            ProjectMergeRecord(project_name = _project_name, integration_id = _integration_id, status = status,
                               desc = _rtn['msg'], update_date = datetime.datetime.now()))
    else:
        _info.integration_id = _integration_id
        _info.status = status
        _info.desc = _rtn['msg']
        _info.update_date = datetime.datetime.now()
    DbCommit()


@api.route('/getProMergeRecordList')
class GetProMergeRecordList(Resource):
    @api.parameters(parameters.GetProMergeRecordListParameters())
    @api.response(schemas.ProjectMergeRecordListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取工程合并主干记录
        :param args:
        :return:
        """
        _project_name = args['project_name']
        _integration_id = args['integration_id']

        _query = ProjectMergeRecord.query
        if _project_name is not None:
            _query = _query.filter_by(project_name = _project_name)
        if _integration_id is not None:
            _query = _query.filter_by(integration_id = _integration_id)

        _list = _query.all()

        return {'success': True, 'msg': '', 'data': _list}


@api.route('/handleProjectSvn')
class HandleProjectSvn(Resource):
    @api.parameters(parameters.HandleProjectSvnParameters())
    @api.response(BaseSchema(many = False))
    def post(self, args, **kwargs):
        """
        添加主干工程时，判断下工程svn是不是按照普通规则来的，如果不是，在数据库里记录
        :param args:
        :return:
        """
        _trunk_name = args['trunk_name']
        _svn_url = args['svn_url']

        if _svn_url is None:
            log.error("app.modules.project.resources.HandleProjectSvn svn地址为空 _trunk_name=%s" % _trunk_name)
            return {'success': True, 'msg': ''}
        _base = '%s/%s/trunk' % (BaseConfig.SVN_URL, _trunk_name)
        if _base == _svn_url:
            log.info("app.modules.project.resources.HandleProjectSvn svn地址符合规则 _trunk_name=%s _svn_url=%s" % (
                _trunk_name, _svn_url))
            return {'success': True, 'msg': ''}
        if not re.search("(https://)([\\s\\S]*)(/%s)(/)(trunk)([\\s\\S]*)" % _trunk_name, _svn_url, re.S):
            log.error("app.modules.project.resources.HandleProjectSvn svn地址不符合规则 _trunk_name=%s _svn_url=%s" % (
                _trunk_name, _svn_url))
            return {'success': True, 'msg': ''}
        _base_svn_url = re.sub("(https://)([\\s\\S]*)(/%s)(/)(trunk)([\\s\\S]*)" % _trunk_name, "\\1\\2\\3", _svn_url)
        _trunk_dir_name = re.sub("(https://)([\\s\\S]*)(/%s)(/)(trunk)([\\s\\S]*)" % _trunk_name, "\\5\\6", _svn_url)

        _info = ProjectSvn.query.filter_by(project_name = _trunk_name).first()

        if _info is not None:
            _info.base_svn_url = _base_svn_url
            _info.trunk_dir_name = _trunk_dir_name
        else:
            db.session.add(ProjectSvn(_trunk_name, _base_svn_url, _trunk_dir_name, 'branches'))
        return DbCommit()
